import sxLunar from './sxLunar'
import dateConfig from './dateConfig.js'

import Gan from '../bagua/gan.js'
import Zhi from '../bagua/zhi.js'
import Ganzhi from '../bagua/ganzhi.js'
import dictionary from '../bagua/dictionary.js'

import utils from '@/utils/index.js'

// 月历工具
let Lunar = null

class CMDate {
  /*
    options 生成日期的参数
    isExtra 是否生成详细万年历
  */
  constructor(options = null, isExtra = false) {
    // 初始化对象
    this.init(options, isExtra)
  }
  
  // 日期对象初始化
  init (options = null, isExtra = false) {
    // enabled参数指明了当前日期是否有用
    this.enabled = true
    // 初始化设定参数
    options = this.initOptions(options)
    if (!utils.obj.isObject(options)) {
      // 没有获取有效的设定参数，说明初始化的数据不合法
      this.enabled = false
      return
    }
    
    // 记录参数
    this.options = options
    this.isExtra = isExtra
    
    // 正式开始初始化
    if (options.lunar) {
      this.initByLunar()
    }
    else {
      this.initBySolar()
    }
  }
  
  // 日期初始化参数检查
  initOptions (options) {
    if (utils.obj.isNull(options) || utils.obj.isUndefined(options)) {
      // 不指定参数，或参数为null、undefined时，选用当前时间
      return CMDate.transDate(new Date())
    }
    if (utils.obj.isDate(options)) {
      // 日期对象转换后返回
      return CMDate.transDate(options)
    }
    if (!utils.obj.isObject(options)) {
      // 除上述两种情况外，options如果不为对象，不合法
      return false
    }
    
    if (utils.obj.isNumber(options.year) && utils.obj.isNumber(options.month) && utils.obj.isNumber(options.day)) {
      // 设置了公历年月日，优先按照公历年月日进行初始化
      return options
    }
    else if (utils.obj.isNumber(options.rulue)) {
      // 如果通过农历选择器初始化日期，则检查儒略日参数
      // 农历的月份存在十三月、后九月、一月等特殊名称的月份，所以只能通过儒略日初始化
      options.lunar = true
      return options
    }
    else {
      // 其余情况均为不合法参数
      return false
    }
  }
  
  // 使用公历数据初始化日期
  initBySolar () {
    const options = this.options
    // 通过寿星万年历获取日期数据
    const dayObj = CMDate.initFromSX(options.year, options.month, options.day)
    if (!dayObj) {
      // 没有获取有效的日期数据，说明数据不合法
      this.enabled = false
      return
    }
    
    // !!!!!重要计算依据，儒略日获取，是以2000年1月1日12:00为起始参照的儒略日
    this.rulue = dayObj.d0
    // 公历年月日
    this.year = dayObj.y
    this.month = dayObj.m
    this.day = dayObj.d
    this.dayIndex = dayObj.di
    this.dayNum = dayObj.dn
    // 日期所在公历月的天数
    this.solarMonthDayNum = dayObj.dn
    // 星期分分钟可以得到
    this.weekDay = dayObj.week
    // 时分必须合法，否则强行重置为00:00
    this.hour = utils.obj.isNumber(options.hour) && options.hour >= 0 && options.hour < dateConfig.HOUR_IN_DAY ? options.hour : 0
    this.min = utils.obj.isNumber(options.min) && options.min >= 0 && options.min < dateConfig.MIN_IN_HOUR ? options.min : 0,
    
    // 农历年月日
    // 以正月初一分界的年份
    this.lunarYear = dayObj.Lyear0 + dateConfig.YEAR_OFFSET
    // 以立春分界的年份
    this.ganzhiYear = dayObj.Lyear + dateConfig.YEAR_OFFSET
    this.lunarMonth = dayObj.Lleap + dayObj.Lmc
    this.lunarDay = dayObj.Ldc
    this.lunarDayIndex = dayObj.Ldi
    // 日期所在农历月的天数
    this.lunarMonthDayNum = dayObj.Ldn
    // 获取农历月的月建地支序数
    this.lunarJian = dayObj.Lmyj
    // 农历月起局序数
    const zhiLen = Zhi.zhiList.length
    this.lunarMonthSign = (dayObj.Lmyj + zhiLen - 1) % zhiLen
    this.lunarMonthSign = this.lunarMonthSign === 0 ? zhiLen : this.lunarMonthSign
    
    
    // 初始化节假日信息
    this.initFestival(dayObj)
    // 初始化节气信息
    this.initTerm(dayObj)
    // 初始化干支纪年
    this.initGanzhi(dayObj)
    
    if (this.isExtra) {
      // 初始化额外万年历信息
      this.initExtras()
    }
  }
  
  // 使用农历数据初始化日期
  initByLunar () {
    // 将儒略日转换成公历
    let newOptions = CMDate.rulueToDate(this.options.rulue)
    this.options.year = newOptions.year
    this.options.month = newOptions.month
    this.options.day = newOptions.day
    this.initBySolar()
  }
  
  initFestival (dayObj) {
    // 从寿星历中获取节日信息
    this.festivals = CMDate.getFestivals(dayObj)
    
    // 今天是否放假
    if (dayObj.Fjia) {
      this.isHoliday = true
    }
    else {
      this.isHoliday = false
    }
  }
  
  // 初始化日期的节气
  initTerm (dayObj) {    
    // 当本日不是节气时，这么判断是因为，用!判断，0也会中招
    // 如果属性不是对象，尽量用hasOwnProperty判断，避免被0、空字符串等坑到
    if (!dayObj.hasOwnProperty('today_jq') || typeof dayObj['today_jq'] != 'number') {
      this.thisTerm = null
      
      var prevIndex = dayObj['prev_jq']
      var prevDayAfterIndex = prevIndex
      var nextIndex = dayObj['next_jq']
      var nextDayAfterIndex = nextIndex
      
      // 例外情况，大雪 ~ 冬至期间，节气天数表的0(冬至)位是今年的冬至而不是明年的冬至，因此应该使用24，24表示的是明年的冬至
      if (prevIndex === 23 && nextIndex === 0) {
        nextDayAfterIndex = prevIndex + 1
      }
    }
    // 当本日是节气时
    else {
      // 分割时间字符串
      var timeArr = dayObj['today_jqsjstr'].split(':')
      // 节气时间精确到分钟
      var hour = parseInt(timeArr[0])
      var min = parseInt(timeArr[1])
      // 赋予本节气信息
      this.thisTerm = {
        index: dayObj['today_jq'],
        name: dateConfig.TERMS.list[dayObj['today_jq']],
        time: {
          year: this.year,
          month: this.month,
          day: this.day,
          hour: hour,
          min: min
        }
      }
      // 初始化
      var prevIndex = dayObj['prev_jq']
      var prevDayAfterIndex = prevIndex
      var nextIndex = dayObj['next_jq']
      var nextDayAfterIndex = nextIndex
      
      // 当前时间在节气时间前，说明还没有到相应的节气
      if (this.hour < hour || (this.hour == hour && this.min < min)) {
        // 处理大雪 ~ 冬至的特殊情况，此时节气表被刷新为新的一年，要注意的是大雪的时间索引
        if (prevIndex == 23 && nextIndex == 0) {
          // 25代表冬至前一个大雪，26代表冬至前一个小雪
          prevDayAfterIndex = 25
        }
      }
      // 反之，当前时间在节气时间后，已经过了相应的节气
      else {
        // 节气推移
        prevIndex = this.thisTerm.index
        prevDayAfterIndex = prevIndex
        nextIndex = (prevIndex + 1) % dateConfig.TERMS.list.length
        nextDayAfterIndex = nextIndex
        
        // 处理大雪 ~ 冬至的特殊情况，节气天数表的0(冬至)位是今年的冬至而不是明年的冬至，因此应该使用24
        if (prevIndex == 23 && nextIndex == 0) {
          nextDayAfterIndex = prevIndex + 1
        }
      }
    }
    
    // 赋值前后节气
    // 千万要注意这个dayAfter差值是根据儒略日计算的，距离当天中午12:00，而不是距离定义的时间
    const prevTime = CMDate.rulueToDate(this.rulue - dayObj['cur_all_jq'][prevDayAfterIndex])
    this.prevTerm = {
      index: prevIndex,
      name: dateConfig.TERMS.list[prevIndex],
      time: prevTime
    }
    const nextTime = CMDate.rulueToDate(this.rulue - dayObj['cur_all_jq'][nextDayAfterIndex])
    this.nextTerm = {
      index: nextIndex,
      name: dateConfig.TERMS.list[nextIndex],
      time: nextTime
    }
  }
  
  initGanzhi (dayObj) {    
    this.ganzhi = []
    // 年月日干支
    let yearGanzhi = new Ganzhi(dayObj.Lyear2[0], dayObj.Lyear2[1], null, {
      description: dictionary.TIME[0] + dictionary.TIMEKEY
    })
    this.ganzhi.push(yearGanzhi)
    let monthGanzhi = new Ganzhi(dayObj.Lmonth2[0], dayObj.Lmonth2[1], null, {
      description: dictionary.TIME[1] + dictionary.TIMEKEY
    })
    this.ganzhi.push(monthGanzhi)
    let dayGanzhi = new Ganzhi(dayObj.Lday2[0], dayObj.Lday2[1], null, {
      description: dictionary.TIME[2] + dictionary.TIMEKEY
    })
    this.ganzhi.push(dayGanzhi)
    
    if (this.thisTerm && this.thisTerm.index % 2 !== 0) {
      // 每逢节,都可能调整月干支
      if (this.nextTerm.index === this.thisTerm.index) {
        // 如果当天是节气,但是时间未到,要调整月干支
        this.ganzhi[1].prev()
        if (this.thisTerm.name === '立春') {
          // 立春是交年日,未到立春还要调年干支
          this.ganzhi[0].prev()
        }
      }
    }
          
    if (this.hour >= 23) {
      // 进入子时,日干支增进
      this.ganzhi[2].next()
    }
    
    // 获取时干支
    const hourZhiIndex = Math.floor(((this.hour + 1) % dateConfig.HOUR_IN_DAY) / 2)
    const hourGanIndex = this.ganzhi[2].gan.getMouseGanIndex(hourZhiIndex)
    const hourGanzhi = new Ganzhi(hourGanIndex, hourZhiIndex, null, {
      description: dictionary.TIME[3] + dictionary.TIMEKEY
    })
    this.ganzhi.push(hourGanzhi)
    
    // 获取刻干支
    let tempMin = this.min
    if (this.hour % 2 == 0) {
      tempMin = tempMin + 60
    }
    const minZhiIndex = Math.floor(tempMin / 10)
    const minGanIndex = this.ganzhi[3].gan.getMouseGanIndex(minZhiIndex)
    const minGanzhi = new Ganzhi(minGanIndex, minZhiIndex, null, {
      description: dictionary.TIME[4] + dictionary.TIMEKEY
    })
    this.ganzhi.push(minGanzhi)
  }
  
  /* @section 一些额外的月历计算方法 */
  // 总的获取额外内容的方法
  initExtras () {
    // 获取星座和月将
    this.initStar()
    // 初始化黄道信息
    this.initHuangdao()
    // 初始化月相信息
    this.initMoon()
  }
  
  // 获取星座和月将
  initStar () {
    const prevTermIndex = this.prevTerm.index
    const index = Math.floor(prevTermIndex / 2)
    this.star = dateConfig.STARS.list[index]
    this.yuejiang = new Zhi(dateConfig.YUEJIANG.list[index])
  }
  
  // 获取当日、当时的黄道信息
  initHuangdao () {
    // 黄道吉日判断
    this.ganzhi[2].initHuangdao(this.ganzhi[1].zhi)
    // 黄道吉时判断
    this.ganzhi[3].initHuangdao(this.ganzhi[2].zhi)
  }
  
  // 获取前、当前、后的月相信息
  initMoon () {
    let thisDay = CMDate.initFromSX(this.year, this.month, this.day)
    if (!thisDay) {
      return
    }
    
    this.thisMoon = null
    this.prevMoon = null
    this.nextMoon = null
    
    if (thisDay.yxmc && thisDay.yxmc !== '') {
      // 指定今日月相
      const index = dateConfig.MOON.list.indexOf(thisDay.yxmc)
      const timeObj = CMDate.solveTimeStr(thisDay.yxsj)
      this.thisMoon = {
        index: index,
        name: thisDay.yxmc,
        time: {
          year: this.year,
          month: this.month,
          day: this.day,
          hour: timeObj.hour,
          min: timeObj.min,
          rulue: this.rulue
        }
      }
      if (this.hour < timeObj.hour || (this.hour === timeObj.hour && this.min < timeObj.min)) {
        // 还没有到指定月相
        this.nextMoon = this.thisMoon
      }
      else {
        this.prevMoon = this.thisMoon
      }
    }
    
    // 初始化月相数据日的集合
    let moonDaysList = [{
      year: this.year,
      month: this.month,
      day: this.day
    }]
    if (!this.prevMoon) {
      // 寻找前一个月相
      let isFinished = false
      let dayIndex = this.dayIndex
      while (!isFinished) {
        if (dayIndex === 0) {
          // 如果已经是当月的首日，则查找上一个月
          const prevMonth = CMDate.getDesMonth(this.year, this.month, -1)
          // 指针指向上一个月
          CMDate.initFromSX(prevMonth.year, prevMonth.month)
          dayIndex = CMDate.Lunar.dn - 1
        }
        else {
          dayIndex--
        }
        const dayObj = CMDate.Lunar.lun[dayIndex]
        // 添加月相日数据
        moonDaysList.unshift({
          year: dayObj.y,
          month: dayObj.m,
          day: dayObj.d
        })
        if (dayObj.yxmc && dayObj.yxmc !== '') {
          const timeObj = CMDate.solveTimeStr(dayObj.yxsj)
          this.prevMoon = {
            index: dateConfig.MOON.list.indexOf(dayObj.yxmc),
            name: dayObj.yxmc,
            time: {
              year: dayObj.y,
              month: dayObj.m,
              day: dayObj.d,
              hour: timeObj.hour,
              min: timeObj.min,
              rulue: dayObj.d0
            }
          }
          isFinished = true
        }
      }      
    }
    
    // 寻找后一个月相
    if (!this.nextMoon) {
      // 指针恢复
      CMDate.initFromSX(this.year, this.month)
      
      let isFinished = false
      let dayIndex = this.dayIndex
      while (!isFinished) {
        if (dayIndex === this.dayNum - 1) {
          // 如果已经是当月的末日，则查找下一个月
          const nextMonth = CMDate.getDesMonth(this.year, this.month, 1)
          // 指针指向下一个月
          CMDate.initFromSX(nextMonth.year, nextMonth.month)
          dayIndex = 0
        }
        else {
          dayIndex++
        }
        const dayObj = CMDate.Lunar.lun[dayIndex]
        // 添加月相日数据
        moonDaysList.push({
          year: dayObj.y,
          month: dayObj.m,
          day: dayObj.d
        })
        if (dayObj.yxmc && dayObj.yxmc !== '') {
          const timeObj = CMDate.solveTimeStr(dayObj.yxsj)
          this.nextMoon = {
            index: dateConfig.MOON.list.indexOf(dayObj.yxmc),
            name: dayObj.yxmc,
            time: {
              year: dayObj.y,
              month: dayObj.m,
              day: dayObj.d,
              hour: timeObj.hour,
              min: timeObj.min,
              rulue: dayObj.d0
            }
          }
          isFinished = true
        }
      }      
    }
    
    // 计算月相偏移
    const len = moonDaysList.length
    moonDaysList = moonDaysList.map((item, index) => {
      if (index === len - 1) {
        // 尾部月相
        return {
          ...item,
          index: this.nextMoon.index,
          name: dateConfig.MOON.list[this.nextMoon.index],
          offset: 0
        }
      }
      if (index === 0) {
        // 头部月相
        return {
          ...item,
          index: this.prevMoon.index,
          name: dateConfig.MOON.list[this.prevMoon.index],
          offset: 0
        }
      }
      return {
        ...item,
        index: this.prevMoon.index,
        offset: index
      }
    })
    this.moonDays = moonDaysList
  }
  
  /* @section 常规静态方法 */
  // 通过寿星万年历获取当日的信息
  static initFromSX (year, month, day = null) {
    if (!CMDate.Lunar) {
      CMDate.Lunar = new sxLunar.Lunar()
    }
    if (CMDate.Lunar.y !== year || CMDate.Lunar.m !== month) {
      // 月历数据不为当月，则进行重新计算
      CMDate.Lunar.yueLiCalc(year, month)
    }
    
    if (!CMDate.Lunar.lun[0].hasOwnProperty('d0')) {
      // 出现空的日数据，说明年月数据不合法，返回false
      return false
    }
    if (day === null) {
      // 没指定日的话，day为默认的null时，直接返回月对象
      return CMDate.Lunar
    }    
    
    
    // 如果指定了日，则对日进行检索
    const dayIndex = CMDate.Lunar.lun.findIndex((obj) => {
      return obj.d === day && obj.m === month && obj.y === year
    })
    if (dayIndex < 0) {
      // 没有找到日，说明日数据不合法，返回false
      return false
    }
    return CMDate.Lunar.lun[dayIndex]      
  }
  
  /*
    给定两个具有 年月日时分 的对象
    如果前者时间超前，a > b，返回true
  */
  static compareTime (a, b) {
    if (a.year < b.year || !a.hasOwnProperty('year')) { return false }
    else if (a.year > b.year || !b.hasOwnProperty('year')) { return true }

    if (a.month < b.month || !a.hasOwnProperty('month')) { return false }
    else if (a.month > b.month || !b.hasOwnProperty('month')) { return true }

    if (a.day < b.day || !a.hasOwnProperty('day')) { return false }
    else if (a.day > b.day || !b.hasOwnProperty('day')) { return true }

    if (a.hour < b.hour || !a.hasOwnProperty('hour')) { return false }
    else if (a.hour > b.hour || !b.hasOwnProperty('hour')) { return true }

    if (a.min < b.min || !a.hasOwnProperty('min')) { return false }
    else if (a.min > b.min || !b.hasOwnProperty('min')) { return true }
    
    return false
  }
  
  // 将时间字符串转换为时间
  static solveTimeStr (str) {
    const arr = str.split(':')
    return {
      hour: parseInt(arr[0]),
      min: parseInt(arr[1])
    }
  }
 
  // 将系统的Date对象转换成本类的options
  static transDate (date) {
    if (!utils.obj.isDate(date)) {
      return null
    }
    
    return {
      year: date.getFullYear(),
      month: date.getMonth() + 1,
      day: date.getDate(),
      hour: date.getHours(),
      min: date.getMinutes(),
      second: date.getSeconds()
    }
  }
  
  // 常规意义上的，获取某个月的天数，不处理特殊情况
  static getMonthDay (year, month) {
    switch(month) {
      case 4:
      case 6:
      case 9:
      case 11:
        return 30
      case 2:
        if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
          return 29
        }
        return 28
      default:
        return 31
    }
  }
  
  // 获取上一个或者下一个月
  static getDesMonth (year, month, offset = 1) {
    month = month + offset
    while (month > dateConfig.MONTH_IN_YEAR) {
      month = month - dateConfig.MONTH_IN_YEAR
      year++
    }
    while (month <= 0) {
      month = month + dateConfig.MONTH_IN_YEAR
      year--
    }
    return { year, month }
  }
  
  /* 
    公历时间转化为儒略日
    前5个，参数太简单，即公历日期，略
    
    @prop rulueOffsetEnabled
    type Boolean
    default true
    是否开启儒略偏移量转换，若为true，输出以2000年1月1日12:00为参照的儒略日
    为false，输出以-4712年1月1日12:00为参照的儒略日
    
    @return
    儒略日 注意是小数    
  */
  static dateToRulue (year, month, day, hour, min, rulueOffsetEnabled = true) {
    const totalMin = hour * dateConfig.MIN_IN_HOUR + min
    const dayMin = dateConfig.HOUR_IN_DAY * dateConfig.MIN_IN_HOUR
    const totalDay = day + (totalMin / dayMin)
    if (rulueOffsetEnabled) {
      return sxLunar.JD.JD(year, month, totalDay) - dateConfig.RULUE_OFFSET
    }
    return sxLunar.JD.JD(year, month, totalDay)
  }
  
  /* 
    儒略日转化为公历时间
    @prop rulue
    type Number(Float)
    儒略日，注意为浮点数
    
    @prop rulueOffsetEnabled
    type Boolean
    default true
    是否开启儒略偏移量转换，若为true，传入的儒略日须以2000年1月1日12:00为参照
    为false，传入的儒略日须以-4712年1月1日12:00为参照
    
    @return
    type Object
    公历 obj.year/month/day/hour/min
  */  
  static rulueToDate (rulue, rulueOffsetEnabled = true) {
    const oldRulue = rulue
    if (rulueOffsetEnabled) {
      rulue = rulue + dateConfig.RULUE_OFFSET
    }
    const timeObj = sxLunar.JD.DD(rulue)
    return {
      year: timeObj.Y,
      month: timeObj.M,
      day: timeObj.D,
      hour: timeObj.h,
      min: timeObj.m,
      rulue: oldRulue
    }
  }
  
  // 从寿星万年历的日数据，解析出节日信息
  static getFestivals (dayObj) {
    // 从寿星历中获取节日信息
    let aFes = dayObj.A.split(' ')
    let bFes = dayObj.B.split(' ')
    let cFes = dayObj.C.split(' ')
    
    // 分割后，每一串的末尾都有''，即空字符串，要去除
    aFes.splice(aFes.length - 1, 1)
    bFes.splice(bFes.length - 1, 1)
    cFes.splice(cFes.length - 1, 1)
    
    // 合并
    let fesList = []
    fesList = fesList.concat(aFes)
    fesList = fesList.concat(bFes)
    fesList = fesList.concat(cFes)
    
    let len = fesList.length
    const fesCheckList = dateConfig.FESTIVALS.list
    for (let i = 0; i < len; i++) {
      // 排除节日列表中没声明的节日
      if (fesCheckList.indexOf(fesList[i]) < 0) {
        fesList.splice(i, 1)
        len--
        i--
      }
    }
    
    return fesList
  }
  
  // 根据农历年，获取该年的所有农历月、天数、月首儒略日
  static getLunarMonths (year) {
    // 初始化年的首月
    let monthData = CMDate.initFromSX(year, 1)
    // 气朔数据
    let ssq = sxLunar.SSQ
    // 月列表长度
    let ymLen = ssq.ym.length
    // 寻找正月的位置
    let headMonthIndex = ssq.ym.indexOf('正')
    // 第二个正月的位置
    const secondTemp = ssq.ym.slice(headMonthIndex + 1).indexOf('正')
    const secondHeadMonthIndex = secondTemp >= 0 ? secondTemp + headMonthIndex + 1 : ymLen
    // 获取月列表，两个正月之间的数据
    let result = {
      lunarMonths: ssq.ym.slice(headMonthIndex, secondHeadMonthIndex),
      lunarMonthsRulue: ssq.HS.slice(headMonthIndex, secondHeadMonthIndex),
      lunarMonthsDays: ssq.dx.slice(headMonthIndex, secondHeadMonthIndex)
    }
    // 闰月名称修正
    if (ssq.leap !== 0 && ssq.leap >= headMonthIndex) {
      result.lunarMonths[ssq.leap - headMonthIndex] = '闰' + result.lunarMonths[ssq.leap - headMonthIndex]
    }
    
    // 初始化明年的首月和气朔
    monthData = CMDate.initFromSX(year + 1, 1)
    ssq = sxLunar.SSQ
    ymLen = ssq.ym.length
    // 明年正月的位置
    headMonthIndex = ssq.ym.indexOf('正')
    // 如果明年正月是月首，则不用再进行修改
    if (headMonthIndex <= 0) {
      return result
    } 
    // 根据明年正月前的信息，对当年的月数据做修正
    for (let i = 0; i < headMonthIndex; i++) {
      const name = ssq.leap === i && ssq.leap !== 0 ? '闰' + ssq.ym[i] : ssq.ym[i]
      // 根据儒略日，找出原来月列表的修正位置
      const fixedIndex = result.lunarMonthsRulue.findIndex(rulue => rulue === ssq.HS[i])
      if (fixedIndex < 0) {
        // 没找到，向尾部添加新元素
        result.lunarMonths.push(name)
        result.lunarMonthsRulue.push(ssq.HS[i])
        result.lunarMonthsDays.push(ssq.dx[i])
      }
      else {
        // 找到了，修正名称
        result.lunarMonths[fixedIndex] = name
      }
     
    }
    return result
  }
  
  // 获取日历三个月的数据，line为日历行数，默认为6行
  static getCalendarMonthData (year, month, line = 6) {
    const prevMonth = CMDate.getDesMonth(year, month, -1)
    const nextMonth = CMDate.getDesMonth(year, month, 1)
    
    return [
      CMDate.getCalendarMonth(prevMonth.year, prevMonth.month, line),
      CMDate.getCalendarMonth(year, month, line),
      CMDate.getCalendarMonth(nextMonth.year, nextMonth.month, line)
    ]
  }
  
  // 获取单个月的日历数据
  static getCalendarMonth (year, month, line = 6) {
    let monthList = []
    // 获取当月的日数据
    let monthData = CMDate.initFromSX(year, month)
    monthList = monthData.lun.slice(0, monthData.dn)
    monthList = monthList.map(day => CMDate.getCalendarDay(day))
    
    // 获取月前数据
    const weekHead = monthList[0].week
    if (weekHead > 0) {
      const prevMonth = CMDate.getDesMonth(year, month, -1)
      monthData = CMDate.initFromSX(prevMonth.year, prevMonth.month)
      let prevMonthList = monthData.lun.slice(monthData.dn - weekHead, monthData.dn)
      prevMonthList = prevMonthList.map(day => CMDate.getCalendarDay(day, 'prev'))
      monthList = prevMonthList.concat(monthList)
    }
    
    // 获取月后数据，补齐
    const len = monthList.length
    const delta = dateConfig.DAY_IN_WEEK * line - len
    if (delta > 0) {
      const nextMonth = CMDate.getDesMonth(year, month, 1)
      monthData = CMDate.initFromSX(nextMonth.year, nextMonth.month)
      let nextMonthList = monthData.lun.slice(0, delta)
      nextMonthList = nextMonthList.map(day => CMDate.getCalendarDay(day, 'next'))
      monthList = monthList.concat(nextMonthList)
    }
    
    return monthList
  }
  
  // 获取日历三个周的数据
  static getCalendarWeekData (year, month, day) {
    return [
      CMDate.getCalendarWeek(year, month, day, -1),
      CMDate.getCalendarWeek(year, month, day),
      CMDate.getCalendarWeek(year, month, day, 1)
    ]
  }
  
  /* 
    获取日历单个周的数据
    year month day 起始参照时间
    offset 该周距离起始参照时间的偏移量
  */
  static getCalendarWeek (year, month, day, offset = 0) {
    let weekList = []
    // 获取当日数据
    let dayData = CMDate.initFromSX(year, month, day)
    // 本周周日的儒略日
    const sundayRulue = dayData.d0 - dayData.week
    // 目标周周日的儒略日
    const desSundayRulue = sundayRulue + offset * dateConfig.DAY_IN_WEEK
    // 转换成日期
    const desDate = CMDate.rulueToDate(desSundayRulue)
    // 跟进日数据
    dayData = CMDate.initFromSX(desDate.year, desDate.month, desDate.day)
    // 计算月末索引
    const monthTailIndex = dayData.di + dateConfig.DAY_IN_WEEK > CMDate.Lunar.dn ? CMDate.Lunar.dn : dayData.di + dateConfig.DAY_IN_WEEK
    // 当月的日子
    weekList = CMDate.Lunar.lun.slice(dayData.di, monthTailIndex)
    weekList = weekList.map(day => CMDate.getCalendarDay(day))
    // 计算缺少的日子
    const delta = dateConfig.DAY_IN_WEEK - monthTailIndex + dayData.di
    // 本月剩余日子不足7天，去下一个月补齐
    if (delta > 0) {
      const nextMonth = CMDate.getDesMonth(desDate.year, desDate.month, 1)
      let monthData = CMDate.initFromSX(nextMonth.year, nextMonth.month)
      let leftWeekDays = monthData.lun.slice(0, delta)
      leftWeekDays = leftWeekDays.map(day => CMDate.getCalendarDay(day))
      weekList = weekList.concat(leftWeekDays)
    }
    return weekList
  }
  
  
  // 获取单日的日历数据
  // type代表状态 standard代表当月的日，prev代表月前补充，next代表月后补充
  static getCalendarDay (day, type = 'standard') {
    let result = {
      rulue: day.d0,
      year: day.y,
      month: day.m,
      day: day.d,
      ganzhi: day.Lday2,
      calendarType: type,
      week: day.week,
      // 事件初始为当日农历名
      event: dateConfig.LUNAR_DAY.list[day.Ldi],
      // 当日是否放假，要作为类名
      holiday: day.Fjia ? 'holiday' : ''
    }
    
    // 如果当日为节气，则节气名作为事件名
    if (day.jqmc && day.jqmc !== '') {
      result.event = day.jqmc
    }
    
    // 如果当日为节日，则节日名作为事件名
    let fesList = CMDate.getFestivals(day)
    fesList = fesList.map((fes) => {
      const index = dateConfig.FESTIVALS.list.indexOf(fes)
      if (index < 0) { return fes }
      else {
        return dateConfig.FESTIVALS.simList[index]
      }
    })
    if (Array.isArray(fesList) && fesList.length > 0) {
      result.event = fesList[0]
    }
    
    return result
  }
  
  // 获取年字符串
  static transYear (year) {
    if (year <= 0) {
      return 'B' + ((year - 1) * (-1))
    }
    return year
  }
  
  // 获取公历日字符串
  static transDay (year, month, day) {
    return CMDate.transYear(year) + '年'
      + month + '月' + day + '日'
  }
  
  // 公历日字符串简写
  static transDaySim (year, month, day, sign = '/') {
    return CMDate.transYear(year) + sign
      + utils.str.numString(month) + sign + utils.str.numString(day)
  }
  
  // 获取时间字符串
  static transTime (hour, min) {
    return utils.str.numString(hour) + ':' + utils.str.numString(min)
  }
  
  static transDateStr (year, month, day, hour, min) {
    return CMDate.transDay(year, month, day) + ' ' + CMDate.transTime(hour, min)
  }
  
  static transDateStrSim (year, month, day, hour, min, sign = '/') {
    return CMDate.transDaySim(year, month, day, sign) + ' ' + CMDate.transTime(hour, min)
  }
  
  static dateToStrSim (dateObj) {
    const obj = CMDate.transDate(dateObj)
    return CMDate.transDateStrSim(obj.year, obj.month, obj.day, obj.hour, obj.min)
  }
  
  /* 
    给定年干，获取十二个月
    yearGan 年干
    extra 需要对每一个月的干支做的额外处理，必须要返回处理后的干支
  */
  static getYearMonths (yearGan, extra = null, options = {}) {
    // 从寅月开始
    let zhiIndex = 2
    
    const len = Zhi.zhiList.length
    let result = []
    for (let i = 0; i < len; i++) {
      const ganIndex = yearGan.getTigerGanIndex(zhiIndex)
      let ganzhi = new Ganzhi(ganIndex, zhiIndex, null, options)
      zhiIndex = (zhiIndex + 1) % 12
      
      // 额外处理干支
      if (typeof extra === 'function') {
        ganzhi = extra(ganzhi, i)
      }
      
      result.push(ganzhi)
    }

    return result
  }
  
  /*
    给定日干，获取十二时辰
    dayGan 日干
    extra 需要对每一个月的干支做的额外处理，必须要返回处理后的干支
  */
  static getDayHours (dayGan, extra = null) {
    // 从子时开始
    let zhiIndex = 0
    
    const len = Zhi.zhiList.length
    let result = []
    for (let i = 0; i < len; i++) {
      const ganIndex = dayGan.getMouseGanIndex(zhiIndex)
      let ganzhi = new Ganzhi(ganIndex, zhiIndex)
      zhiIndex = (zhiIndex + 1) % 12
      
      // 额外处理干支
      if (typeof extra === 'function') {
        ganzhi = extra(ganzhi)
      }
      
      result.push(ganzhi)
    }

    return result
  }
  
  /* @section get方法 */
  // 获取当天的十二时辰，并计算黄道情况
  getDayHours () {
    return CMDate.getDayHours(this.ganzhi[2].gan, (ganzhi) => {
      // 计算黄道的配置
      ganzhi.initHuangdao(this.ganzhi[2].gan.index)
      return ganzhi
    })
  }
  
  // 获取当天对应的所有节气信息
  getYearTerms () {
    const dayObj = CMDate.initFromSX(this.year, this.month, this.day)
    if (!dayObj) {
      return []
    }
    
    if (!this.dayAfterAllTerms || !Array.isArray(this.dayAfterAllTerms)) {
      this.dayAfterAllTerms = []
      // 拷贝当日距离当年所有节气的天数，范围：上一个小雪到下一个冬至
      let dayAfterAllTerms = utils.obj.jsonClone(dayObj['cur_all_jq'])
      // 详细解构
      const len = dayAfterAllTerms.length
      const termsLen = dateConfig.TERMS.list.length
      for (let i = 0; i < len; i++) {
        const time = this.rulue - dayAfterAllTerms[i]
        let obj = {
          time: CMDate.rulueToDate(time)
        }
        if (i <= termsLen) {
          obj.index = i % termsLen
          obj.name = dateConfig.TERMS.list[obj.index]
          this.dayAfterAllTerms.push(obj)
        }
        else {
          // 25代表冬至前一个大雪，26代表冬至前一个小雪
          obj.index = termsLen * 2 - i
          obj.name = dateConfig.TERMS.list[obj.index]
          this.dayAfterAllTerms.unshift(obj)
        }
      }
    }
    
    return this.dayAfterAllTerms
  }
  
  /*
    获取 上一个小雪到下一个冬至 范围内，某一个节气的时间
    24代表后一个冬至，25代表冬至前一个大雪，26代表冬至前一个小雪
  */
  getYearTermTime (index) {
    console.log(this)
    const dayObj = CMDate.initFromSX(this.year, this.month, this.day)
    if (!dayObj) {
      return []
    }
    
    const time = this.rulue - dayObj['cur_all_jq'][index]
    return CMDate.rulueToDate(time)
  }
  
  // 获取当日的日出日落
  getSun (location) {
    const lon = utils.cal.toRad(location.lon)
    const lat = utils.cal.toRad(location.lat)
    const utc = location.utc * (-1)
    sxLunar.SZJ.calcRTS(this.rulue, 1, lon, lat, utc)
    const obj = sxLunar.SZJ.rts[0]
    const result = {
      rise: obj.s.slice(0, 5),
      down: obj.j.slice(0, 5)
    }
    const riseTime = CMDate.solveTimeStr(result.rise)
    const downTime = CMDate.solveTimeStr(result.down)
    result.riseMin = riseTime.hour * dateConfig.MIN_IN_HOUR + riseTime.min
    result.downMin = downTime.hour * dateConfig.MIN_IN_HOUR + downTime.min
    
    return result
  }
  
  // 获取当月农历月的索引
  getLunarMonthIndex () {
    const data = CMDate.getLunarMonths(this.lunarYear)
    return data.lunarMonths.indexOf(this.lunarMonth)
  }
  
  // 获取公历日字符串
  getDayStr (sign = '/') {
    return [this.getYear(), utils.str.numString(this.month), utils.str.numString(this.day)].join(sign)
  }
  
  // 获取公历日期字符串
  getDateStr (sign = '/') {
    return this.getDayStr(sign) + ' ' + this.getTime()
  }
  
  // 获取公历日期设定
  getSolarOptions () {
    return {
      year: this.year,
      month: this.month,
      day: this.day,
      hour: this.hour,
      min: this.min
    }
  }
  
  // 年存在公元前的问题，this.year不够完善，所以输出时要用到此方法
  getYear () {
    return CMDate.transYear(this.year)
  }
  
  getYearName () {
    return this.getYear() + '年'
  }
  
  getMonthName () {
    return this.month + '月'
  }
  
  getDayName () {
    return this.day + '日'
  }
  
  getTime () {
    return CMDate.transTime(this.hour, this.min)
  }
  
  getSolarName () {
    return CMDate.transDay(this.year, this.month, this.day)
  }
  
  
  getWeekDay () {
    return '星期' + dateConfig.WEEKDAY.list[this.weekDay]
  }
  
  getLunarYearName () {
    let result = ''
    let yearText = this.lunarYear.toString()
    if (this.lunarYear <= 0) {
      result = '前'
      yearText = this.lunarYear * (-1) + 1
      yearText = yearText.toString()
    }
    const textList = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
    const len = yearText.length
    for (let i = 0; i < len; i++) {
      result = result + textList[parseInt(yearText[i])]
    }
    return result + '年'
  }
  
  getLunarMonthName () {
    return this.lunarMonth + '月'
  }
 
  getLunarName () {
    return this.getLunarYearName() + this.getLunarMonthName() + this.lunarDay
  }
  
  // 获取干支文字,用空格分开
  getGanzhiText () {
    return this.ganzhi.slice(0, 4).map(ganzhi => ganzhi.name).join(' ')
  }
  
  // 节气获取
  getTermRange () {
    return this.prevTerm.name + ' ~ ' + this.nextTerm.name
  }
  
  getThisTermTime () {
    if (this.thisTerm) {
      return CMDate.transTime(this.thisTerm.time.hour, this.thisTerm.time.min)
    }
    return ''
  }
  
  getPrevTermTime () {
    const timeObj = this.prevTerm.time
    return CMDate.transDateStr(timeObj.year, timeObj.month, timeObj.day, timeObj.hour, timeObj.min)
  }
  
  getNextTermTime () {
    const timeObj = this.nextTerm.time
    return CMDate.transDateStr(timeObj.year, timeObj.month, timeObj.day, timeObj.hour, timeObj.min)
  }
  
  // 判断当前时间是阳气为主(冬至-夏至)还是阴气为主(夏至-冬至)
  isSolar () {
    if (this.prevTerm.index >= Math.floor(dateConfig.TERMS.list.length / 2)) {
      return false
    }
    return true
  }
}

Object.assign(CMDate, { 
  config: dateConfig, 
  Lunar,
})

export default CMDate